package logic;

import java.util.List;
import java.util.LinkedList;

public class GLCClass{
    private List<SintaxRule> sr;
    private String id;
    private List<Simbol> simbols;

    public GLCClass (){
	this.sr=new LinkedList<SintaxRule>();
	this.id="";
	this.simbols=new LinkedList<Simbol>();
    }
    
    public GLCClass(String id){
	this.id=id;
	this.sr=new LinkedList<SintaxRule>();
	this.simbols=new LinkedList<Simbol>();
    }

    public GLCClass(String id, List<SintaxRule> sr){
	this.id=id;
	this.sr=sr;
	this.simbols=new LinkedList<Simbol>();
    }
    
    public GLCClass(String id, List<Simbol> simbols, List<SintaxRule> sr){
	this.id=id;
	this.sr=sr;
	this.simbols=simbols;
    }
    
    public List<SintaxRule> getSintaxRule(){
	return this.sr;
    }

    public List<Simbol> getSimbols(){
	return this.simbols;
    }

    public String getId(){
	return this.id;
    }

    public void setId(String id){
	this.id=id;
    }

    public void addSR(SintaxRule s){
	this.sr.add(s);
    }
    
    public void addSimbol(Simbol s){
	this.simbols.add(s);
    }

    public void setType(String type){
	for(int i=0;i<simbols.size();i++)
	    simbols.get(i).setType(type);
    }
    
    public void addSimbols(GLCClass g2){
	for(int i=0;i<g2.getSimbols().size();i++)
	    this.addSimbol(g2.getSimbols().get(i));
    }
    /*
    public List<SintaxRule> GLC2EBNF2(){
    	List<SintaxRule> srs = new LinkedList<SintaxRule>();
	    for(int i=0;i<sr.size();i++)
	    	srs.add(sr.get(i).GLC2EBNF());
	    return srs;
    }
    */
    
    /*	grammar a{
			terminal a, b, c, d, e;
			nonterminal A;
			A := d | A a c  | b A | ; 
		}

		operations{
			glc2ebnf(a)
		}
     */
    
    public List<SintaxRule> GLC2EBNF(){
    	List<SintaxRule> srs = new LinkedList<SintaxRule>();
    	SintaxRule csr;
		SintaxRule s;
		int index;
		String position;
		boolean recursion, centerRecursion;
		Production p, innerp;
		
		/* Para cada sintaxrule de la GCL */
    	for(int l=0;l<sr.size();l++){
    		 csr=new SintaxRule(sr.get(l).getId());
    		 index=-1;
    		 if(sr.get(l).getId().contains("AUX")){
    			 csr=sr.get(l).clone();
    		 }else{
	    		 /* Para cada produccion de la sintaxrule */
	    		 for(int i=0;i<sr.get(l).getProduction().size();i++){
				    p=new Production();
				    recursion=false;
				    centerRecursion=false;
				    /* Miramos los simbolos de la produccion */
				    for(int j=0;j<sr.get(l).getProduction().get(i).getSimbols().size() && !recursion;j++){
				    	/* Si es distinto del no terminal que se esta definiendo */
						if(!sr.get(l).getProduction().get(i).getSimbols().get(j).getValue().equals(sr.get(l).getId())){
						    p.addSimbol(sr.get(l).getProduction().get(i).getSimbols().get(j));
						}else{ /* Si es igual que el no terminal que se esta definiendo */
							//Se borra la produccion
							p=new Production();
							
							
							/* Se pone la repeticion en el lugar contrario al lugar en el que se encuentra 
							 * la recursion, para poner los casos de parada en su lugar */
							index=sr.get(l).getProduction().get(i).getSimbols().size()-j-1;
							if(index==0) index=1;
							else if(index==sr.get(l).getProduction().get(i).getSimbols().size()-1)
								index=0;
							else
								centerRecursion=true;
							
							// Se insertan los simbolos de las otras producciones sin repeticiones si las hay
							if(!centerRecursion && sr.get(l).isThereNonConflictingProduction()){
								s=new SintaxRule();
								s.setPosition(1-index); //Se colocan en el lugar contrario al de las repeticiones
								s.setType("grouped"); //En principio los casos de parada se ponen en grouped
								/* Para todas las producciones que no contengan el no terminal que se esta definiendo */
								for(int m=0;m<sr.get(l).getProduction().size();m++){
									innerp=new Production();
									if(!sr.get(l).getProduction().get(m).contains(sr.get(l).getId()))
										/* Si es una produccion vacia se pone el tipo de los casos de parada optional */
										if(sr.get(l).getProduction().get(m).isEmpty())
											s.setType("optional");
										else{ /* Se insertan los casos de parada */
											for(int ms=0;ms<sr.get(l).getProduction().get(m).getSimbols().size();ms++)
												innerp.addSimbol(sr.get(l).getProduction().get(m).getSimbols().get(ms));
											s.addProduction(innerp);
										}
								}
								p.addSRSimbol(s);
							}
							
							if(centerRecursion){
								//Casos en los que la recursion este en el centro
								/* Para todas los simbolos de la produccion */
								for(int m=0;m<sr.get(l).getProduction().get(i).getSimbols().size();m++){
									//Se insertan los anteriores a la recursion
									if(!sr.get(l).getProduction().get(i).getSimbols().get(m).equals(sr.get(l).getId()))
										p.addSimbol(sr.get(l).getProduction().get(i).getSimbols().get(m));
									else{//Cuando se llega a la recursion
										innerp=new Production();
										s=new SintaxRule();
										//Se crea un nuevo simbolo
										s.setId("AUX"+sr.get(l).getId()+j);
										getSimbols().add(new Simbol("AUX"+sr.get(l).getId()+j,"nonterminal"));
										//Se insertan todos los simbolos posteriores a la recursion
										for(int n=m+1;n<sr.get(l).getProduction().get(i).getSimbols().size();n++,m++)
											innerp.addSimbol(sr.get(l).getProduction().get(i).getSimbols().get(n));
										
										//Si hay produccion vacia se insertan estos simbolos como caso de parada
										if(sr.get(l).isThereEmptyProduction())
											s.addProduction(innerp.clone());
										
										innerp.addFinalSimbol(new Simbol(sr.get(l).getId(),"nonterminal"));
										s.addProduction(innerp);
										
										addSR(s);
										p.addSimbol(new Simbol("AUX"+sr.get(l).getId()+j,"nonterminal"));
									}
								}
							}else{
								//Se inserta un SintaxRule con repeticiones equivalente a la recursion
							    s=new SintaxRule();
							    s.setPosition(index);
							    s.setType("repeated");
							    innerp=new Production();
							    for(int n=0;n<sr.get(l).getProduction().get(i).getSimbols().size();n++)
							    	if(!sr.get(l).getProduction().get(i).getSimbols().get(n).equals(csr.getId()))
							    		innerp.addSimbol(sr.get(l).getProduction().get(i).getSimbols().get(n));
							    s.addProduction(innerp);
							    p.addSRSimbol(s);
							}
						    recursion=true;
						}
				    }
				    if(!p.isEmpty())
				    	csr.addProduction(p);
				}
				
				//Miramos la primera produccion con repeticiones
				index=-1;
				for(int prod=0;prod<csr.getProduction().size();prod++){
					index=-1;
					position="";
					if(csr.getProduction().get(prod).haveRepetition())
						index=prod;
					if(index!=-1){
						position=csr.getProduction().get(index).repetitionSide();
						//Se unen todas las producciones con repeticiones por el mismo sitio
						for(int prod2=0;prod2<csr.getProduction().size();prod2++)
							if(prod2!=index && csr.getProduction().get(prod2).haveRepetition())
								if(position.equals(csr.getProduction().get(prod2).repetitionSide())){
									for(int n=0;n<csr.getProduction().get(prod2).getRepSRSimbol().getProduction().size();n++)
										csr.getProduction().get(index).getRepSRSimbol().addProduction(csr.getProduction().get(prod2).getRepSRSimbol().getProduction().get(n));
									csr.getProduction().remove(prod2);
									prod2--;
								}
					}
				}
				
				//Miramos si existen producciones con repeticiones por distinto lugar
				index=-1;
				for(int prod=0;prod<csr.getProduction().size();prod++){
					index=-1;
					position="";
					if(csr.getProduction().get(prod).haveRepetition())
						index=prod;
					if(index!=-1){
						position=csr.getProduction().get(index).repetitionSide();
						//Se unen todas las producciones con repeticiones por el distinto sitio
						for(int prod2=0;prod2<csr.getProduction().size();prod2++)
							if(prod2!=index && csr.getProduction().get(prod2).haveRepetition())
								if(!position.equals(csr.getProduction().get(prod2).repetitionSide())){
									csr.getProduction().get(index).addSRSimbol(csr.getProduction().get(prod2).getRepSRSimbol());
									csr.getProduction().remove(prod2);
									prod2--;
								}
					}
				}
				
				//Miramos si existen producciones con recursion en dos pasos
				//TODO
    		 }
    		 
    		 
    		 srs.add(csr);
    	}
		return srs;
    }


    public String toString(){
	String termin="";
	String nontermin="";
	boolean haytermin=false;
	boolean haynontermin=false;

	String s="grammar "+getId()+"{\n";

	for(int i=simbols.size()-1;i>=0;i--){
	    if(simbols.get(i).getType()=="terminal"){
	    	if(haytermin==true)termin+=", ";
	    	termin+=simbols.get(i).getValue();
			haytermin=true;
	    }
	    if(simbols.get(i).getType()=="nonterminal"){
	    	if(haynontermin==true)nontermin+=", ";
			nontermin+=simbols.get(i).getValue();
			haynontermin=true;
	    }
	}

	if(haytermin==true)s+="terminal "+termin+";\n";
	if(haynontermin==true)s+="nonterminal "+nontermin+";\n";

	for(int i=sr.size()-1;i>=0;i--)
	    s+=sr.get(i).toString()+"\n";

	s+="}\n";

	return s;
    }	
}